#include <assert.h>
#include <iostream>
#include <stdio.h>
#include "openserver.h"
#include "opencomhttpclient.h"
#include "opencomhttpserver.h"

using namespace open;

class HttpApp :public OpenApp
{
    int balance_;
    OpenServer* server_;
    std::vector<OpenServer*> accepts_;
    std::vector<OpenServer*> clients_;

    static HttpApp OpenApp_;
public:
    static inline HttpApp& Instance() { return OpenApp_; }
    virtual void start()
    {
        OpenApp::start();

        OpenTimer::Run();
        OpenServer::RegisterCom<OpenComHttpServer>("OpenComHttpServer");
        OpenServer::RegisterCom<OpenComHttpAgent>("OpenComHttpAgent");
        OpenServer::RegisterCom<OpenComHttpClient>("OpenComHttpClient");

        accepts_ = {
            OpenServer::StartServer("OpenHttpAgent1", { "OpenComHttpAgent" }),
            OpenServer::StartServer("OpenHttpAgent2", { "OpenComHttpAgent" }),
            OpenServer::StartServer("OpenHttpAgent3", { "OpenComHttpAgent" }),
            OpenServer::StartServer("OpenHttpAgent4", { "OpenComHttpAgent" })
        };
        server_ = OpenServer::StartServer("OpenHttpServer", { "OpenComHttpServer" });
        assert(server_);

        balance_ = 0;
        clients_ = {
            OpenServer::StartServer("OpenHttpClient1", { "OpenComHttpClient" }),
            OpenServer::StartServer("OpenHttpClient2", { "OpenComHttpClient" }),
            OpenServer::StartServer("OpenHttpClient3", { "OpenComHttpClient" }),
            OpenServer::StartServer("OpenHttpClient4", { "OpenComHttpClient" })
        };
    }
    bool httpServer(std::shared_ptr<OpenHttpServerMsg>& protoMsg)
    {

        for (size_t i = 0; i < accepts_.size(); i++)
            protoMsg->vectAccepts_.push_back(accepts_[i]->pid());

        for (size_t i = 0; i < clients_.size(); i++)
            protoMsg->vectClients_.push_back(clients_[i]->pid());

        auto proto = std::shared_ptr<OpenMsgProto>(new OpenMsgProto);
        proto->msg_ = protoMsg;
        bool ret = OpenThread::Send(server_->pid(), proto);
        assert(ret);
        return ret;
    }
    bool httpClient(std::shared_ptr<OpenMsgProtoMsg>& protoMsg, bool isAll)
    {
        if (clients_.empty())
        {
            assert(false);
            return false;
        }
        //isAll
        auto proto = std::shared_ptr<OpenMsgProto>(new OpenMsgProto);
        proto->msg_ = protoMsg;
        bool ret = false;
        if (isAll)
        {
            for (size_t i = 0; i < clients_.size(); i++)
            {
                ret = OpenThread::Send(clients_[i]->pid(), proto);
                assert(ret);
            }
        }
        else
        {
            if (balance_ >= clients_.size()) balance_ = 0;

            OpenServer* client = clients_[balance_++];
            ret = OpenThread::Send(client->pid(), proto);
            assert(ret);
        }
        return ret;
    }
    bool httpClient(std::shared_ptr<OpenHttpRequest>& request)
    {
        if (clients_.empty())
        {
            assert(false);
            return false;
        }
        auto protoMsg = std::shared_ptr<OpenHttpClientSyncMsg>(new OpenHttpClientSyncMsg);
        protoMsg->request_ = request;
        if (balance_ >= clients_.size())
        {
            balance_ = 0;
        }
        OpenServer* client = clients_[balance_++];
        auto proto = std::shared_ptr<OpenMsgProto>(new OpenMsgProto);
        proto->msg_ = protoMsg;
        bool ret = OpenThread::Send(client->pid(), proto);
        assert(ret);
        protoMsg->openSync_.await();
        return ret;
    }
};
HttpApp HttpApp::OpenApp_;

void OnOpenHttpHandle(OpenHttpRequest& req, OpenHttpResponse& resp)
{
#ifdef USE_OPEN_SSL
    //80 redirect to 443
    if (req.listenPort_ == 80)
    {
        resp.code_ = 302;
        resp["location"] = "https://" + req.host_ + ":" + std::to_string(req.port_) + "/" + req.url_;
        return;
    }
#endif
    resp.response(".html", "<html><body><h1>HelloWorld</h1>"
        "<p>Welcome to OpenLinyou</p></body></html>");
}

int main()
{
    HttpApp::Instance().start();

    printf("start HttpServer\n");
    auto msg = std::shared_ptr<OpenHttpServerMsg>(new OpenHttpServerMsg);
    msg->ip_ = "0.0.0.0";
#ifdef USE_OPEN_SSL
    msg->port_ = 443;
    msg->port1_ = 80;
    msg->isHttps_ = 1;
    msg->keyFile_ = "www.xx.com.key";
    msg->certFile_ = "www.xx.com.crt";
#else
    msg->port_ = 80;
    msg->port1_ = 0;
    msg->isHttps_ = 0;
#endif
    msg->handle_ = &OnOpenHttpHandle;
    HttpApp::Instance().httpServer(msg);

    //httpclient
    OpenThread::Sleep(500);

    auto request = std::shared_ptr<OpenHttpRequest>(new OpenHttpRequest);
    request->method_ = "GET";
#ifdef USE_OPEN_SSL
    request->url_ = "https://www.xx.com/";
    (*request)["client"] = "https://www.bing.com/";
#else
    request->url_ = "http://127.0.0.1/";
    (*request)["client"] = "http://www.bing.com/";
#endif
    HttpApp::Instance().httpClient(request);

    auto& response = request->response_;
    std::string head;
    response.getHead(head);
    printf("[http client]code:%d, header:%s\n", response.code_, head.data());
    std::string body;
    response.getBody(body);
    printf("[http client]body:%s\n", body.data());

    HttpApp::Instance().wait();

    return getchar();
}